/*
 * Copyright 2009 Armando Blancas
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
package tests.taskgraph.channels;

import java.io.EOFException;
import java.io.IOException;

import junit.framework.Assert;
import junit.framework.JUnit4TestAdapter;

import org.junit.After;
import org.junit.Before;
import org.junit.Test;

import taskgraph.channels.CharChannel;
import taskgraph.ports.CharInputPort;
import taskgraph.ports.CharOutputPort;

public class CharChannelTest {

	/**
	 * Runs the test suite in this class from the command line.
	 * 
	 * @param args	Arguments are ignored.
	 */
	public static void main(String[] args) {
		org.junit.runner.JUnitCore.runClasses(CharChannelTest.class);
	}

	/**
	 * Provides compatibility with 3.x versions of JUnit.
	 * 
	 * @return A 3.x-compatible test suite.
	 */
	public static junit.framework.Test suite() {
		return new JUnit4TestAdapter(CharChannelTest.class);
	}

	private static int DEFAULT_BUFFER_SIZE = 1024 * 32;

	private CharInputPort input;
	private CharOutputPort output;

    /*+----------------------------------------------------------------------+
	  |                                                                      | 
	  | Test the constructors.                                               | 
	  |                                                                      | 
	  +----------------------------------------------------------------------+*/
	
	@Test
	public void defaultConstructor() {
		CharChannel ch = new CharChannel();
		Assert.assertNotNull(ch);
	}
	
	@Test
	public void defaultInstanceClass() {
		CharChannel ch = new CharChannel();
		Assert.assertEquals(CharChannel.class, ch.getClass());
	}

	@Test
	public void constructorWithSize() {
		CharChannel ch = new CharChannel(DEFAULT_BUFFER_SIZE);
		Assert.assertNotNull(ch);
	}
	
	@Test
	public void instanceClassWithSize() {
		CharChannel ch = new CharChannel(DEFAULT_BUFFER_SIZE);
		Assert.assertEquals(CharChannel.class, ch.getClass());
	}
	
    /*+----------------------------------------------------------------------+
	  |                                                                      | 
	  | Test the write and read methods.                                     | 
	  |                                                                      | 
	  +----------------------------------------------------------------------+*/
	
	@Before
	public void setup() throws IOException {
		CharChannel channel = new CharChannel(DEFAULT_BUFFER_SIZE);			
		output = channel.getOutputPort();
		input = channel.getInputPort();
	}
	
	@After
	public void cleanup() throws IOException {
		input.close();
		output.close();
	}
	
	@Test
	public void receive() throws IOException {
		output.write('a');
		output.write('b');
		output.write('c');
		output.write('d');
		output.write('f');
		output.write('g');
		Assert.assertEquals(6, input.skipChars(6));
	}
	
	@Test
	public void receiveHighLoad() throws IOException {
		final int size = DEFAULT_BUFFER_SIZE - 1024;
		for (int i = 0; i < size; ++i) {
			output.write('X');
		}
		Assert.assertEquals(size, input.skipChars(size));
	}
	
	@Test
	public void read() throws IOException {
		char[] data = "The quick fox jumped over the lazy dog".toCharArray();
		output.write(data);
		for (char c : data)
			Assert.assertEquals(c, input.read());
	}
	
	@Test
	public void readArray() throws IOException {
		char[] data = "The quick fox jumped over the lazy dog".toCharArray();
		output.write(data);
		char[] buffer = new char[data.length];
		input.read(buffer);
		for (int i = 0; i < data.length; ++i)
			Assert.assertEquals(data[i], buffer[i]);
	}
	
	@Test
	public void readSubArray() throws IOException {
		// Write the second part of the quote
		String s1 = "The wise man said: ";
		String s2 = "Now is the time!";
		output.write(s2.toCharArray());
		
		// Allocate an array that will hold the full quote;
		// Copy s1 but get s2 from the input port.
		char[] quote = new char[s1.length()+s2.length()];
		for (int i = 0; i < s1.length(); ++i)
			quote[i] = s1.charAt(i);		
		input.read(quote, s1.length(), s2.length());
		
		// 'quote' must be = with s1+s2
		char[] expected = (s1 + s2).toCharArray();
		for (int i = 0; i < quote.length; ++i)
			Assert.assertEquals(expected[i], quote[i]);
	}
	
	@Test
	public void writeSubArray() throws IOException {
		String s = "The quick fox jumped over the lazy dog";
		char[] data = s.toCharArray();
		char[] sub = s.substring(10, 34).toCharArray();
		output.write(data, 10, 24);
		char[] buffer = new char[50];
		int count = input.read(buffer);
		for (int i = 0; i < count; ++i)
			Assert.assertEquals(sub[i], buffer[i]);
	}

	@Test
	public void skip() throws IOException {
		int size = 1024;
		int skip = 512;
		for (int i = 0; i < size; ++i) {
			output.write('X');
		}
		input.skipChars(skip);
		Assert.assertEquals(size-skip, input.skipChars(512));
	}
	
	@Test
	public void clear() throws IOException {
		int size = 1024;
		for (int i = 0; i < size; ++i) {
			output.write('X');
		}
		input.skipChars(input.skipChars(size));
		Assert.assertEquals(0, input.skipChars(1));
	}
	
	@Test(expected=EOFException.class)
	public void readTheEnd() throws IOException {
		char[] data = "The quick fox jumped over the lazy dog".toCharArray();
		output.write(data);
		output.close();
		input.read(data);
		input.read(); // read one more than write: EOFException
	}
	
	@Test
	public void readLines() throws IOException {
		StringBuilder b = new StringBuilder("Now is the time.")
			.append('\n')
		 	.append("The quick brown fox\r\n")
			.append("jumped over the\n")
			.append("lazy dog\n")
			.append('\n')
			.append("\n*")
			.append("Now is really the time\r")
			.append("for a break\n")
			.append("\n\r\n")
			.append("The End.\n");
		
		String[] lines = {
				"Now is the time.",
				"The quick brown fox",
				"jumped over the",
				"lazy dog",
				"",
				"",
				"*Now is really the time",
				"for a break",
				"",
				"",
				"The End."
		};
			
		output.write(b.toString().toCharArray());
		for (int i = 0; i < lines.length; ++i) {
			Assert.assertEquals(lines[i], input.readLine());
		}
	}

	@Test
	public void readLastLine() throws IOException {
		char[] data = "The quick fox\njumped over the lazy dog\n".toCharArray();
		output.write(data);
		output.close();
		Assert.assertEquals("The quick fox", input.readLine());
		Assert.assertEquals("jumped over the lazy dog", input.readLine());
		Assert.assertEquals(null, input.readLine());
	}
	
    /*+----------------------------------------------------------------------+
	  |                                                                      | 
	  | Negative tests for constructors.                                     | 
	  |                                                                      | 
	  +----------------------------------------------------------------------+*/
	
	@Test(expected=IllegalArgumentException.class)
	public void constructorWithZeroSize() throws IOException {
		new CharChannel(0);
	}
	
    /*+----------------------------------------------------------------------+
	  |                                                                      | 
	  | Negative tests for the write and read methods.                       | 
	  |                                                                      | 
	  +----------------------------------------------------------------------+*/

	@Test(expected=NullPointerException.class)
	public void writeNullArray() throws IOException {
		output.write(null);
	}
	
	@Test(expected=NullPointerException.class)
	public void readNullArray() throws IOException {
		input.read(null);
	}
	
	@Test(expected=IndexOutOfBoundsException.class)
	public void tryReadSubArray() throws IOException {
		String s = "Now is the time!";
		output.write(s.toCharArray());		
		char[] quote = new char[s.length()];
		input.read(quote, 0, s.length()+1); // off-by-one error
	}
	
	@Test(expected=IndexOutOfBoundsException.class)
	public void tryWriteSubArray() throws IOException {
		String s = "Now is the time!";
		output.write(s.toCharArray(), 1, s.length()); // off-by-one error
	}

}
